package ar.edu.utn.frba.disenio.aerolineas;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import ar.edu.utn.frba.disenio.Asiento;
import ar.edu.utn.frba.disenio.Busqueda;
import ar.edu.utn.frba.disenio.DateParser;
import ar.edu.utn.frba.disenio.Escalador;
import ar.edu.utn.frba.disenio.ReservasManager;
import ar.edu.utn.frba.disenio.Usuario;
import ar.edu.utn.frba.disenio.Vuelo;
import ar.edu.utn.frba.disenio.exceptions.NoSePuedeComprarException;
import ar.edu.utn.frba.disenio.exceptions.NoSePuedeReservarException;
import ar.edu.utn.frba.disenio.tipousuarios.TipoUsuarioExterno;

import com.oceanic.AerolineaOceanic;
import com.oceanic.AsientoDTO;

public class OceanicAdapter implements Aerolinea {

	private AerolineaOceanic oceanic;
	private ReservasManager reservasManager = new ReservasManager();
	
	public OceanicAdapter() {
		super();
		this.oceanic = AerolineaOceanic.getInstance();
		this.obtenerReservasExternas();
	}
	

	public OceanicAdapter(AerolineaOceanic mock) {
		super();
		oceanic = mock;
	}
	
	public String toString()
	{
		return "Oceanic";
	}

	@Override
	public List<Vuelo> asientosDisponibles(Usuario usuario, Busqueda busqueda) {
				
		DateTimeFormatter dateFormat = DateTimeFormat.forPattern("dd/MM/YYYY");
		List<Vuelo> vuelos = new ArrayList<Vuelo>();
		String origenAdaptado = adaptarDestinoOrigen(busqueda.getOrigen());
		String destinoAdaptado = adaptarDestinoOrigen(busqueda.getDestino());
		
		List<Asiento> asientosDirectos = this.adaptarAsientos(this.asientosDisponiblesParaOrigenYDestino(origenAdaptado, destinoAdaptado,busqueda.getFecha().toString(dateFormat)),usuario);
		
		if(busqueda.isConEscalas())
		{	
			List<Asiento> asientosOrigen = this.adaptarAsientos(oceanic.asientosDisponiblesParaOrigen(origenAdaptado, busqueda.getFecha().toString(dateFormat)),usuario);
			
			List<Asiento> asientosIntermedios = new ArrayList<Asiento>();
			List<Asiento> asientosDestino = new ArrayList<Asiento>();
			
			for (Asiento asiento : asientosOrigen)
			{
				String origen = adaptarDestinoOrigen(asiento.getDestino());
				List<Asiento> intermediosPosibles = this.adaptarAsientos(oceanic.asientosDisponiblesParaOrigen(origen, asiento.getLlegada().toString(dateFormat)),usuario);
				for(Asiento intermedioPosible : intermediosPosibles)
				{
					if(intermedioPosible.getDestino().equals(destinoAdaptado))
					{
						asientosDestino.add(intermedioPosible);
					}
					else
					{
						asientosIntermedios.add(intermedioPosible);
					}
				}
			}
			
			for (Asiento asiento : asientosIntermedios)
			{
				String origen = adaptarDestinoOrigen(asiento.getDestino());
				asientosDestino.addAll(this.adaptarAsientos(oceanic.asientosDisponiblesParaOrigenYDestino(origen, destinoAdaptado,asiento.getLlegada().toString(dateFormat)),usuario));
			}
			
			List<Vuelo> unaEscala = Escalador.unaEscala(asientosOrigen,asientosDestino);
			List<Vuelo> dosEscalas = Escalador.dosEscalas(asientosOrigen,asientosIntermedios,asientosDestino);
			
			vuelos.addAll(unaEscala);
			vuelos.addAll(dosEscalas);
		}
		List<Vuelo> sinEscalas = Escalador.sinEscala(asientosDirectos);
		vuelos.addAll(sinEscalas);
		

		return vuelos;
	}
	
	
	private List<AsientoDTO> asientosDisponiblesParaOrigenYDestino(
			String origenAdaptado, String destinoAdaptado, String fechaDeSalida)
	{
		
		List<AsientoDTO> disponibles = new ArrayList<AsientoDTO>();
		
		for (AsientoDTO asiento : oceanic.getAsientos()) 
		{
			if(estaDisponible(origenAdaptado, destinoAdaptado, fechaDeSalida, asiento))
				disponibles.add(asiento);
		}
		
		return disponibles;
	}


	private boolean estaDisponible(String origenAdaptado,
			String destinoAdaptado, String fechaDeSalida, AsientoDTO asiento) {
		return asiento.getOrigen().equals(origenAdaptado) && asiento.getDestino().equals(destinoAdaptado) && asiento.getFechaDeSalida().equals(fechaDeSalida) && !oceanic.estaComprado(asiento.getCodigoDeVuelo(), asiento.getNumeroDeAsiento());
	}


	private void obtenerReservasExternas() 
	{	
		Usuario usuarioExterno = new Usuario(null, null, new TipoUsuarioExterno(), null);
		
		List<Asiento> asientos = this.adaptarAsientos(this.oceanic.getAsientos(), usuarioExterno);
		for(Asiento asiento : asientos)
		{
			if(asiento.getReservado())
			{
				this.reservasManager.reservar(asiento, usuarioExterno);
				//Mando a reservar los asientos reservados externamente porque no estan anotados en Oceanic.
				this.oceanic.reservar("Externo", asiento.getCodigoVuelo(), asiento.getNumeroAsiento());
			}
		}
	}
	
	
	public String adaptarDestinoOrigen(String destinoOrigen)
	{
		 return destinoOrigen.equals("LA") ? "SLA" : (destinoOrigen.length() == 2 ? "_"+destinoOrigen : destinoOrigen);
	}
	
	private List<Asiento> adaptarAsientos(List<AsientoDTO> asientosOceanic, Usuario usuario)
	{
		List<Asiento> asientosAdaptados = new ArrayList<Asiento>();
		
		for (AsientoDTO asiento : asientosOceanic)
		{
			Asiento asientoAdaptado = new Asiento();
			asientoAdaptado.setAerolinea(this);
			asientoAdaptado.setCodigoVuelo(asiento.getCodigoDeVuelo());
			asientoAdaptado.setNumeroAsiento(asiento.getNumeroDeAsiento());
			asientoAdaptado.setPrecio(asiento.getPrecio(),new BigDecimal(0),usuario.getAdicionalPorTipo());
			asientoAdaptado.setClase(String.valueOf(asiento.getClase().charAt(0)));
			asientoAdaptado.setUbicacion(String.valueOf(asiento.getUbicacion().charAt(0)));
			asientoAdaptado.setReservado(asiento.getReservado());
			asientoAdaptado.setLlegada(DateParser.parseaFlexiblemente(asiento.getFechaDeLlegada()+" "+asiento.getHoraDeLlegada()));
			asientoAdaptado.setSalida(DateParser.parseaFlexiblemente(asiento.getFechaDeSalida()+" "+asiento.getHoraDeSalida()));
			asientoAdaptado.setOrigen(asiento.getOrigen());
			asientoAdaptado.setDestino(asiento.getDestino());
			
			asientosAdaptados.add(asientoAdaptado);
		}
		
		return asientosAdaptados;
	}

	@Override
	public void comprarAsiento(Usuario usuario, Asiento asiento)
	{
		this.reservasManager.controlDeVentaDeReserva(asiento,usuario);
		if (!this.oceanic.comprarSiHayDisponibilidad(usuario.getDni().toString(), asiento.getCodigoVuelo(), asiento.getNumeroAsiento()))
		{
			throw new NoSePuedeComprarException("No hay disponibilidad para el asiento solicitado.");
		}
		this.reservasManager.comprarReserva(asiento, usuario);
	}

	@Override
	public BigDecimal getPorcentajeImpuestos() {
		return new BigDecimal(0);
	}

	@Override
	public void reservarAsiento(Usuario usuario,Asiento asiento)
	{
		//Tira excepcion si no puede reservar.
		reservasManager.ValidarReserva(asiento, usuario);
		
		if(this.oceanic.reservar(usuario.getDni().toString(),asiento.getCodigoVuelo(),asiento.getNumeroAsiento()))
			this.reservasManager.reservar(asiento, usuario);
		else 
			throw new NoSePuedeReservarException("Ocurrio un error al intentar reservar el asiento.");
	}
	
	@Override
	public void sobrereservarAsiento(Usuario usuario,Asiento asiento)
	{
		this.reservasManager.ValidarSobrereserva(asiento, usuario);
		this.reservasManager.sobrereservar(asiento, usuario);
	}
	
	@Override
	public void cancelarReserva(Asiento asiento, Usuario usuario) {
		this.reservasManager.cancelarReservacion(asiento, usuario);
		
	}

}
